/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.chain.shell;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import tal.drivechain.chain.script.ScriptCommand;

/**
 *
 * @author eisental
 */
public abstract class ShellObject extends ShellCommand {
    HashMap<String, Method> actions;
    String name;
    String plural;
    String abb;

    public enum Action {
        make, remove, set, list, inspect
    }

    public ShellObject(String name, String plural, String abb, DriveShell shell) {
        super(shell);
        this.name = name; this.plural = plural; this.abb = abb;
        addActionMethods();
    }

    public boolean runCommand(ScriptCommand command) throws IllegalArgumentException {
        // extract the action name
        if (!command.getArg1().equals(name) && !command.getArg1().equals(plural) && !command.getArg1().equals(abb))
            return false;

        Action action;
        try {
            action = Action.valueOf(command.getName().trim());
        } catch (IllegalArgumentException ie) {
            tryCustomCommand(command.getName().trim(), command.getArg2());
            return true;
        }
        if (!isActionSupported(action))
            throw new IllegalArgumentException("Unsupported action on object " + name);

        if (action!=Action.list && command.getArg1().equals(plural) && (!plural.equals(abb)) && (!plural.equals(name)))
            throw new IllegalArgumentException("Use " + action.name() + " " + name + 
                    (!name.equals(abb)?" or " + abb:"") + " instead.");
        else if (action==Action.list) {
            if (!command.getArg1().equals(plural) && (command.getArg1().equals(name) || command.getArg1().equals(abb)))
                throw new IllegalArgumentException("Use list " + plural + " instead.");
        }
        
        switch (action) {
            case make:
                make(command.getArg2().trim());
                break;
            case remove:
                remove(command.getArg2().trim());
                break;
            case set:
                set(command.getArg2().trim());
                break;
            case list:
                list();
                break;
            case inspect:
                inspect(command.getArg2().trim());
                break;
        }
        return true;

    }

    private void tryCustomCommand(String action, String command) {
        if (actions==null)
            throw new IllegalArgumentException("Unknown " + name + " action: " + action);
        for (String a : actions.keySet()) {
            if (a.equals(action)) {
                try {
                    actions.get(a).invoke(this, command);
                } catch (IllegalAccessException ex) {
                    ex.printStackTrace();
                    throw new IllegalArgumentException("Error while invoking custom shell object command: " + ex.getMessage());
                } catch (IllegalArgumentException ex) {
                    ex.printStackTrace();
                    throw new IllegalArgumentException("Error while invoking custom shell object command: " + ex.getMessage());
                } catch (InvocationTargetException ex) {
                    ex.getTargetException().printStackTrace();
                    throw new IllegalArgumentException(actions.get(a).getName() + " " + name + ": " + ex.getTargetException().getMessage());
                }
                return;
            }
        }

        throw new IllegalArgumentException("Unknown " + name + " action: " + action);

    }

    public abstract boolean isActionSupported(Action action);
    public abstract void make(String command) throws IllegalArgumentException;
    public abstract void remove(String command) throws IllegalArgumentException;
    public abstract void set(String command) throws IllegalArgumentException;
    public abstract void list() throws IllegalArgumentException;
    public abstract void inspect(String command) throws IllegalArgumentException;

    private void addActionMethods() {
        HashMap<String, Method> a = new HashMap<String, Method>();
        Method[] ms = this.getClass().getMethods();
        for (Method m : ms) {
            if (m.getReturnType() == void.class &&
                    m.getParameterTypes().length==1 &&
                    m.getParameterTypes()[0]==String.class) {
                boolean iscustom = true;
                for (Action c : Action.values())
                    if (m.getName().equals(c.toString()))
                        iscustom = false;
                if (iscustom)
                    a.put(m.getName(), m);
            }
        }
        if (a.size()>0) {
            actions = a;
        } else actions = null;
    }


    @Override
    public String[] getCommands() {
        List<String> list = new ArrayList<String>();
        for (Action action : Action.values()) {
            if (this.isActionSupported(action)) {
                if (action!=Action.list)
                    list.add(action.name() + " " + name);
            }
        }
        
        if (actions!=null) {
            for (String c : actions.keySet())
                list.add(c + " " + name);
        }

        return list.toArray(new String[] { });
    }

}
